/**
 * 一、将下面异步代码使用Promise的方式改进
 *   setTimeout(function () {
 *       var a = 'hello';
 *       setTimeout(function () {
 *           var b = 'lagou';
 *           setTimeout(function () {
 *               var c = 'I ❤ U';
 *               console.log(a + b + c);
 *           }, 10)
 *       }, 10)
 *   }, 10)
 */
const fn = (value) =>
  new Promise((resolved) => {
    setTimeout(() => {
      resolved(value);
    }, 10);
  });

fn()
  .then(() => fn("hello"))
  .then((value) => fn(value + "lagou"))
  .then((value) => fn(value + "I ❤ U"))
  .then((value) => {
    console.log(value);
  });

/**
 * 二、基于以下代码完成下面的四个练习
 * 数据 horsepower 马力，dollar_value 价格，in_stock 库存
 *   [{name:'Ferrart FF',horsepower:660,dollar_value:700000,in_stock:true},
 *   {name:'Spyker C12 Zagato',horsepower:650,dollar_value:648000,in_stock:false},
 *   {name:'Jagur XKR-S',horsepower:550,dollar_value:132000,in_stock:false},
 *   {name:'Audi R8',horsepower:525,dollar_value:114200,in_stock:false},
 *   {name:'Aston Martin One-77',horsepower:750,dollar_value:1850000,in_stock:true},
 *   {name:'Pagani Huayra',horsepower:700,dollar_value:1300000,in_stock:false},
 *   ]
 */

/* 练习1: 使用函数组合fp.flowRight()重新实现下面这个函数
let isLaskInStock = function (cars){
    // 获取最后一条数据
    let last_car = fp.last(cars)
    // 获取最后一条数据的in_stock属性值
    return fp.prop('in_stock',last_car)
} */
let fr = fp.flowRight(fp.prop("in_stock"), fp.last);
console.log(fr(cars));

/* 练习2：使用函数组合fp.flowRight()、fp.prop()和fp.first()获取第一个car的name */
let fr = fp.flowRight(fp.prop("name"), fp.first);
console.log(fr(cars));

/* 练习3：使用帮助函数_average重构averageDollarValue，使用函数组合的方式实现 */
let _average = (xs) => fp.reduce(fp.add, 0, xs) / xs.length;
let averageDollarValue = (cars) => {
  let arr = fp.flowRight(
    _average,
    fp.map((car) => car.dollar_value)
  );
  return arr(cars);
};
console.log(averageDollarValue(cars));

/* 
练习4：使用flowRight写一个sanitizeNames()函数，返回一个下划线连接的小写字符串，
把数组中的name转换为这种形式，例如：sanitizeNames([‘Hello World’]) => [‘Hello_world’]
*/
let _underscore = fp.replace(/\W+/g, "_");
const sanitizeNames = () => fp.flowRight(fp.map((name) => _underscore(name)));
console.log(sanitizeNames(["Hello World"]));

/**
 * 三、基于下面提供的代码，完成后续的四个练习
 * class Container {
    static of(value){
        return new Container(value)
    }
    constructor(value){
        this._value = value
    }
    map(fn){
        return Container.of(fn(this._value))
    }
}
class MayBe{
    static of(x){
        return new MayBe(x)
    }
    isNothing(){
        return this._value === null || this._value === undefined
    }
    constructor(x){
        this._value = x
    }
    map(fn){
        return this.isNothing()?this:MayBe.of(fn(this._value))
    }
}
module.exports = {MayBe,Container}
*/

/* 练习1：使用fp.add(x,y)和fp.map(f,x)创建一个能让functor里的值增加的函数ex1 */
const fp = require("lodash/fp");
const { parseInt } = require("lodash");
let maybe = MayBe.of([5, 6, 1]);
let ex1 = () => {
  let fn = fp.flowRight(fp.map(fp.add(num)));
  return maybe.map(fn);
};
console.log(ex1(3));

/* 练习2: 实现一个函数ex2，能够使用fp.first获取列表的第一个元素*/
let xs = Container.of(["do", "ray", "me", "fa", "so", "la", "ti", "do"]);
let ex2 = () => {
  return xs.map(fp.first)._value;
};
console.log(ex2());

/* 练习3: 实现一个函数ex3，使用safeProp和fp.first找到user的名字的首字母 */
let ex3 = () => safeProp("name", user).map(fp.first)._value;
console.log(ex3());

/* 练习4: 使用MayBe重写ex4，不要有if语句
let  ex4 = function(n){
    if(n){
        return parseInt (n)
    }
} */
let ex4 = (n) => {
  let m1 = new MayBe(n);
  let m2 = m1.map(parseInt);
  return m2._value;
};
console.log(ex4(1));

/* 四、手写实现MyPromise源码
要求：尽可能还原Promise中的每一个API，并通过注释的方式描述思路和原理 */

/**
 * 1、promise是一个类，在执行这个类的时候，需要传递一个执行器进去，执行器会立即执行
 * 2、Promise中有三个状态，分别为 成功 resolve、失败 reject、等待 pending
 *    状态一旦确定就不能被改变
 *    pending-resolve
 *    pending-reject
 * 3、resolve和reject函数是用来更改状态的
 *    resolve:fufilled
 *    reject:rejected
 * 4、then方法做的事情就是判断状态，如果状态是成功，调用成功回调函数，如果是失败，调用失败函数，then方法是被定义在原型对象中
 * 5、then成功回调有一个参数，表示成功之后的值，失败回调有一个参数，表示失败的原因
 */
const PENDING = "pending"; //等待
const FULFILLED = "fufilled"; //成功
const REJECT = "reject"; //失败

class MyPromise {
  constructor(exeuctor) {
    try {
      exeuctor(this.resolve, this.reject);
    } catch (error) {
      this.reject(error);
    }
  }

  status = PENDING;
  //成功之后的值
  value = undefined;
  //失败之后的原因
  reason = undefined;
  //成功回调
  // successCallback = undefined  只能处理一个回调函数
  successCallback = [];
  //失败回调
  // failCallback = undefined
  failCallback = [];

  //使用箭头函数定义是为了执行方法的时候让this指向MyPromise的实例对象
  resolve = (value) => {
    //如果状态不是等待，向下执行
    if (this.status !== PENDING) return;
    this.status = FULFILLED;
    //保存成功之后的值
    this.value = value;
    //判断成功回调是否存在，如果存在则调用
    // this.successCallback && this.successCallback(this.value)
    while (this.successCallback.length) {
      // this.successCallback.shift()(this.value)
      this.successCallback.shift()();
    }
  };

  reject = (reason) => {
    if (this.status !== PENDING) return;
    this.status = REJECT;
    //保存失败后的原因
    this.reason = reason;
    // this.failCallback && this.failCallback(this.reason)
    while (this.failCallback.length) {
      // this.failCallback.shift()(this.reason)
      this.failCallback.shift()();
    }
  };

  then(successCallback, failCallback) {
    successCallback = successCallback ? successCallback : (value) => value;
    failCallback = failCallback
      ? failCallback
      : (reason) => {
          throw reason;
        };
    let promise2 = new MyPromise((resolve, reject) => {
      if (this.status === FULFILLED) {
        // let x = successCallback(this.value)
        /**
         * 需要判断x的值是普通值还是promise对象,如果是普通值，直接调用resolve，
         * 如果是promise对象，查看promise的结果，根据promise对象返回的结果决定调用resolve，reject
         */
        // resolvePromise(x,resolve,reject)
        // 防止循环调用,但是此时promise2并不能获取到，所以现在需要使其变成异步执行代码
        // resolvePromise(promise2,x,resolve,reject)
        //使用try-catch捕获异常
        try {
          setTimeout(() => {
            let x = successCallback(this.value);
            resolvePromise(promise2, x, resolve, reject);
          }, 0);
        } catch (error) {
          reject(error);
        }
      } else if (this.status === REJECT) {
        setTimeout(() => {
          let x = failCallback(this.reason);
          resolvePromise(promise2, x, resolve, reject);
        }, 0);
      } else {
        // 状态为pedding，等待
        // 将成功回调存储起来
        this.successCallback.push(() => {
          setTimeout(() => {
            let x = successCallback(this.value);
            resolvePromise(promise2, x, resolve, reject);
          }, 0);
        });
        // 将失败回调存储起来
        this.failCallback.push(() => {
          setTimeout(() => {
            let x = failCallback(this.value);
            resolvePromise(promise2, x, resolve, reject);
          }, 0);
        });
      }
    });
    return promise2;
  }

  finally(callback) {
    return this.then(
      (value) => {
        return MyPromise.resolve(callback()).then(() => value);
      },
      (reason) => {
        return MyPromise.resolve(callback()).then(() => {
          throw reason;
        });
      }
    );
  }

  catch(failCallback) {
    return this.then(undefined, failCallback);
  }

  static all(array) {
    let result = [];

    return new MyPromise((resolve, reject) => {
      let count = 0;
      function addData(index, value) {
        result[index] = value;
        count++;
        if (count === array.length) {
          resolve(result);
        }
      }
      for (let i = 0; i < array.length; i++) {
        let current = array[i];
        if (current instanceof MyPromise) {
          //Promise对象
          current.then(
            (value) => {
              addData(i, value);
            },
            (reason) => {
              reject(reason);
            }
          );
        } else {
          //普通值
          addData(i, current);
        }
      }
    });
  }

  static resolve(value) {
    if (value instanceof MyPromise) {
      return value;
    }
    return new MyPromise((resolve) => resolve(value));
  }
}

function resolvePromise(promise, x, resolve, reject) {
  if (promise === x) {
    return reject(
      new TypeError("Chaining cycle detected for promise #<Promise>    ")
    );
  }
  if (x instanceof MyPromise) {
    x.then(resolve, reject);
  } else {
    resolve(x);
  }
}

module.exports = MyPromise;
